package com.huayi.datagovernance.utils.io;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.*;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.huayi.datagovernance.utils.fastjson.User;
import org.junit.Test;

import java.io.*;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;

/**
 * Io工具-IoUtil
 * IO工具类的存在主要针对InputStream、OutputStream、Reader、Writer封装简化，并对NIO相关操作做封装简化。
 * 总体来说，Hutool对IO的封装，主要是工具层面，我们努力做到在便捷、性能和灵活之间找到最好的平衡点。
 * @author wuch
 * @version 1.0
 * @date 2021/4/19 18:49
 */
public class IoUtilDemo {
    /**
     * 拷贝
     * 流的读写可以总结为从输入流读取，从输出流写出，这个过程我们定义为拷贝。
     * 这个是一个基本过程，也是文件、流操作的基础。
     */
    @Test
    public void test1() throws IOException {
        //以文件流拷贝为例：
        BufferedInputStream in = FileUtil.getInputStream("C:/Users/Administrator/Desktop/0407/test.txt");
        BufferedOutputStream out = FileUtil.getOutputStream("C:/Users/Administrator/Desktop/0407/test2.txt");
        //copy(InputStream in, OutputStream out, int bufferSize) 拷贝流，拷贝后不关闭流
        long copySize = IoUtil.copy(in, out, IoUtil.DEFAULT_BUFFER_SIZE);
        Console.log(copySize);

        //copy(InputStream in, OutputStream out, int bufferSize, StreamProgress streamProgress)
        // StreamProgress 进度监视
        long copySize1 = IoUtil.copy(in, out, IoUtil.DEFAULT_BUFFER_SIZE, new StreamProgress() {
            @Override
            public void start() {
                Console.log("开始拷贝...");
            }

            @Override
            public void progress(long size) {
                Console.log("读取缓冲区大小："+ size);
            }

            @Override
            public void finish() {
                Console.log("拷贝结束...");
            }
        });
        Console.log(copySize1);
        //copy方法同样针对Reader、Writer、Channel等对象有一些重载方法，并提供可选的缓存大小。
        // 默认的，缓存大小为1024个字节，如果拷贝大文件或流数据较大，可以适当调整这个参数。

//        针对NIO，提供了copyByNIO方法，以便和BIO有所区别。我查阅过一些资料，使用NIO对文件流的操作有一定的提升，
//        我并没有做具体实验。相关测试请参阅博客：http://www.cnblogs.com/gaopeng527/p/4896783.html
//        copy(FileInputStream in, FileOutputStream out) 拷贝文件流，使用NIO
        long copySize2 = IoUtil.copy(IoUtil.toStream(new File("C:/Users/Administrator/Desktop/0407/test.txt")),
                new FileOutputStream("C:/Users/Administrator/Desktop/0407/test2.txt"));
        Console.log(copySize2);


//       	copy(InputStream in, OutputStream out, int bufferSize)
//        将Reader中的内容复制到Writer中 使用默认缓存大小，拷贝后不关闭Reader
        StringReader reader = new StringReader("。。胜多负少的");
        StringWriter writer = new StringWriter();
       /* int readSize;
        while ((readSize = reader.read()) != -1){
            writer.write(readSize);
        }
        Console.log(writer.toString());*/
        long copySize3 = IoUtil.copy(reader, writer, IoUtil.DEFAULT_BUFFER_SIZE);
        Console.log("内容：{}，大小：{}",writer.toString(),copySize3);
        IoUtil.flush(writer);//从缓存中刷出数据 相当于 out.flush
        IoUtil.close(in);//关闭 关闭失败不会抛出异常
        IoUtil.closeIfPosible(in);//尝试关闭指定对象 判断对象如果实现了AutoCloseable，则调用之
    }

    /**
     * 获取输入，输出流
     */
    @Test
    public void test4() throws IOException {
        /*输入流*/
//        getUtf8Reader(InputStream in) 获得一个文件读取器，默认使用UTF-8编码
//        getReader(Reader reader) 如果是BufferedReader强转返回，否则新建。如果提供的Reader为null返回null，默认UTF-8
//        getReader(InputStream in, Charset charset) 获得一个Reader
//        getUtf8Reader(InputStream in) 获得一个文件读取器，默认使用UTF-8编码
        byte[] bytes = StrUtil.bytes("超级无敌大力丸", CharsetUtil.CHARSET_UTF_8);
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        BufferedReader reader = IoUtil.getReader(in, CharsetUtil.CHARSET_UTF_8);
        Console.log(reader.readLine());

        /*输出流*/
//        getUtf8Writer(OutputStream out) 获得一个Writer，默认编码UTF-8
//        getWriter(OutputStream out, Charset charset) 获得一个Writer，默认编码UTF-8


        //流转缓冲输入流 BufferedInputStream、BufferedReader
//        toBuffered(InputStream in) 转换为BufferedInputStream
//        toBuffered(InputStream in, int bufferSize) 转换为BufferedInputStream
//        toBuffered(Reader reader) BufferedReader
//        toBuffered(Reader reader, int bufferSize) BufferedReader

        //流转缓冲输出流 BufferedOutputStream、BufferedWriter
//        toBuffered(OutputStream out) 转换为BufferedOutputStream
//        toBuffered(OutputStream out, int bufferSize) 转换为BufferedOutputStream
//        toBuffered(Writer writer) 转换为BufferedWriter
//        toBuffered(Writer writer, int bufferSize) 转换为BufferedWriter

        //字节流 ByteArrayInputStream
        ByteArrayInputStream in1 = IoUtil.toStream(bytes); //byte[] 转为ByteArrayInputStream
        ByteArrayInputStream in2 = IoUtil.toStream("超级无敌大力丸", CharsetUtil.CHARSET_UTF_8); //字符串 转 字节
        ByteArrayInputStream in3 = IoUtil.toUtf8Stream("超级无敌大力丸"); //字符串 转 字节

        //文件流 FileInputStream
        FileInputStream fis = IoUtil.toStream(new File(""));//文件流
    }

    /**
     * Stream转Reader、Writer
     * IoUtil.getReader：将InputStream转为BufferedReader用于读取字符流，它是部分readXXX方法的基础。
     * IoUtil.getWriter：将OutputStream转为OutputStreamWriter用于写入字符流，它是部分writeXXX的基础。
     * 本质上这两个方法只是简单new一个新的Reader或者Writer对象，但是封装为工具方法配合IDE的自动提示可以大大减少查阅次数（例如你对BufferedReader、OutputStreamWriter不熟悉，
     * 是不需要搜索一下相关类？）
     */

    /**
     * 读取流中的内容
     * 读取流中的内容总结下来，可以分为read方法和readXXX方法。
     *
     * 1. read方法有诸多的重载方法，根据参数不同，可以读取不同对象中的内容，这包括：
     * InputStream
     * Reader
     * FileChannel
     *
     * 2. 这三个重载大部分返回String字符串，为字符流读取提供极大便利。
     * readXXX方法主要针对返回值做一些处理，例如：
     * readBytes 返回byte数组（读取图片等）
     * readHex 读取16进制字符串
     * readObj 读取序列化对象（反序列化）
     * readLines 按行读取
     *
     * 3. toStream方法则是将某些对象转换为流对象，便于在某些情况下操作：
     * String 转换为ByteArrayInputStream
     * File 转换为FileInputStream
     */
    @Test
    public void test2() throws IOException, ClassNotFoundException {
        BufferedInputStream in = FileUtil.getInputStream("C:/Users/Administrator/Desktop/0407/test.txt");
//        1. read(InputStream in) 从流中读取内容，读到输出流中，读取完毕后并不关闭流
        FastByteArrayOutputStream read = IoUtil.read(in);
//        Console.log(read.toString()+read.toString(CharsetUtil.UTF_8)+read.toByteArray());

//        2. read(InputStream in, Charset charset) 从流中读取内容，读取完毕后并不关闭流
        String read1 = IoUtil.read(in, CharsetUtil.UTF_8);
        Console.log(read1);

        StringReader reader = new StringReader("。。胜多负少的");
//        从Reader中读取String，读取完毕后关闭Reader
//        3. String read2 = IoUtil.read(reader);
//        Console.log(read2);

        FileInputStream fis = IoUtil.toStream(new File("C:/Users/Administrator/Desktop/0407/test.txt"));
        FileChannel fileChannel = fis.getChannel();
//        4. 从FileChannel中读取String，读取完毕后关闭Reader
        String read3 = IoUtil.read(fileChannel, "UTF-8");
        Console.log(read3);

//        5. readBytes(InputStream in) 从流中读取bytes，读取完毕后关闭流
        Console.log(IoUtil.readBytes(in).length);;
//        6. readBytes(InputStream in, int length) 读取指定长度的byte数组，不关闭流
        Console.log(IoUtil.readBytes(fis, 1000).length);
//        7. readBytes(InputStream in, boolean isClose) 从流中读取bytes，读取完毕后关闭流
//        Console.log(IoUtil.readBytes(fis, true).length);

//        8. readHex(InputStream in, int length, boolean toLowerCase) 读取16进制字符串

//        9. readLines(Reader reader, T collection) 按行读取数据，针对每行的数据做处理 Reader自带编码定义，因此读取数据的编码跟随其编码。 往集合追加内容
        List<String> list = ListUtil.list(false);
        List<String> list1 = IoUtil.readLines(reader, list);
        Console.log(list1);//[。。胜多负少的]

        FileInputStream fis1 = IoUtil.toStream(new File("C:/Users/Administrator/Desktop/0407/test2.txt"));
//        10. readLines(InputStream in, Charset charset, T collection) 从流中读取内容 往集合追加内容
//        List<String> list2= IoUtil.readLines(fis1, CharsetUtil.UTF_8, list);
//        Console.log(list2);//[。。胜多负少的, 艾斯DVD撒都是都是都是]

//       11. readLines(InputStream in, Charset charset, LineHandler lineHandler) 按行读取数据，针对每行的数据做处理
        StringBuilder builder = StrUtil.builder();
        IoUtil.readLines(fis1, CharsetUtil.CHARSET_UTF_8, new LineHandler() {
            @Override
            public void handle(String s) {
                Console.log("处理字符串：{}",s);
                builder.append(s).append("@");
            }
        });
//        Console.log(builder.toString());

//       12. readObj(InputStream in) 从流中读取对象，即对象的反序列化 inputstream 必须为 ObjectInputStream 或 子类
//        String baseStr = "{\"ab\":\"sdfjsdklv现场库V领就算了的框架\"}";
//        ByteArrayInputStream bin = new ByteArrayInputStream(baseStr.getBytes(CharsetUtil.CHARSET_UTF_8));
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:/Users/Administrator/Desktop/0407/testobject.txt"));
        ArrayList<User> users = new ArrayList<User>();
        users.add(new User("23","张三",null));
        users.add(new User("24","李四",null));
//        IoUtil.writeObj(oos, true, users);
        oos.writeObject(users);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:/Users/Administrator/Desktop/0407/testobject.txt"));
        ArrayList<User> readusers = (ArrayList<User>) ois.readObject();
        Console.log(readusers);

    }

    /**
     * 其他API
     */
    @Test
    public void test3(){
        /*contentEquals(Reader input1, Reader input2)
         对比两个Reader的内容是否一致
         内部会转换流为 BufferedInputStream*/
        StringReader reader1 = new StringReader("第三方看电视了CR");
        StringReader reader2 = new StringReader("第三方看电视了");
        boolean b = IoUtil.contentEquals(reader1, reader2);
           /*contentEqualsIgnoreEOL(Reader input1, Reader input2)
        对比两个流内容是否相同，忽略EOL字符
        内部会转换流为 BufferedInputStream*/
        boolean b1 = IoUtil.contentEqualsIgnoreEOL(reader1, reader2);
        Console.log(b);//false
        Console.log(b1);//true


    }
}
